package server;

import java.net.InetAddress;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Random;

public class ClientList
{
	private LinkedList<Client> clients = new LinkedList<Client>();
	private ServerGUI gui;

	public ClientList(ServerGUI gui)
	{
		this.gui = gui;
	}

	public void addClient(InetAddress address, int port)
	{
		clients.add(new Client(address, port, System.currentTimeMillis()));
	}

	public void updateTimeToDie(String address, int port, long dieAt)
	{
		for (Client c : clients)
		{
			if(c.address.getHostAddress().equals(address) && c.port == port)
			{
				c.dieAt = dieAt;
			}
		}
	}

	public String createRandomPeerList(int maxNumber, InetAddress address, int port)
	{
		StringBuilder list = new StringBuilder();
		//if more are wanted than what we've got, return them all
		if(maxNumber >= clients.size() - 1)
		{
			for (Client c : clients)
			{
				if(!(address.getHostAddress().equals(c.address.getHostAddress()) && port == c.port))
				{
					if(list.length() > 0)
					{
						list.append(';');
					}
					list.append(c.address.getHostAddress() + ":" + c.port);
				}
			}
		}
		else //if the client list is larger than the clients we want to return
		{
			Random r = new Random(System.nanoTime());
			double p = maxNumber / (clients.size() - 1); //the probability [range: 0.0-1.0] of any single client being returned
			int inList = 0;
			boolean getNext = false; //if we select one which has already been selected, select the next one (will help speed this up when the list size is only slightly larger than the maxNumber)
			ListIterator<Client> i = clients.listIterator();
			while(inList < maxNumber)
			{
				if(!i.hasNext()) //reached end of list, go back to start
				{
					i = clients.listIterator();
				}
				Client c = i.next();
				if(!(address.getHostAddress().equals(c.address.getHostAddress()) && port == c.port))
				{
					if(!c.in && (r.nextDouble() < p || getNext)) //if the client hasn't been added, and the dice says we should
					{
						if(list.length() > 0)
						{
							list.append(';');
						}
						list.append(c.address.getHostAddress() + ":" + c.port);
						c.in = true;
						getNext = false;
						inList++;
					}
					else if(c.in)
					{
						getNext = true;
					}
				}
			}
			for (Client c : clients) //unmark which clients were returned
			{
				c.in = false;
			}
		}
		return list.toString();
	}

	public void removeDeadClients(long now)
	{
		LinkedList<Client> removeList = new LinkedList<Client>();
		
		for (Client c : clients)
		{
			if(c.dieAt < now)
			{
				removeList.add(c);
				gui.removeConnection(c.toString());
			}
		}
		
		for(Client c: removeList)
		{
			clients.remove(c);
		}
	}

	public int size()
	{
		return clients.size();
	}

	public Client get(int i)
	{
		if(i < clients.size())
		{
			return clients.get(i);
		}
		else
		{
			return null;
		}
	}
	
	public class Client
	{
		InetAddress address;
		int port;
		long dieAt;
		
		boolean in = false;
		
		public Client(InetAddress address, int port, long dieAt)
		{
			this.address = address;
			this.port = port;
			this.dieAt = dieAt;
		}
		
		public String toString()
		{
			return address.getHostAddress() + ":" + port;
		}
	}
}
